#include "stringextension.hpp"
#include <string>
#include <vector>
#include <algorithm>
#include <stdexcept>

EString::EString(const std::string& s)
    : std::string(s)
{} 

EString::EString(const char* s)
    : std::string(s)
{}

EString::EString(std::string&& s)
    : std::string(std::move(s))
{}
    
void EString::replace(const EString& from, const EString& to) 
{
	if (from.empty()) return;

	size_t start_pos = 0;
	while ((start_pos = find(from, start_pos)) != std::string::npos) 
	{
		std::string::replace(start_pos, from.length(), to);
		start_pos += to.length();
	}
}

EString EString::replaced(const EString& from, const EString& to) 
{
	EString newStr(*this);
	newStr.replace(from, to);
	return newStr;
}

EStringList EString::split(const EString& sep) const 
{
	if (sep.empty()) 
		throw std::invalid_argument("Separator cannot be empty");

	EStringList result;
	size_t start = 0, end = 0;

	while ((end = find(sep, start)) != std::string::npos) 
	{
		result.emplace_back(substr(start, end - start));
		start = end + sep.length();
	}
	result.emplace_back(substr(start));

	return result;
}

bool EString::startsWith(const EString& prefix) const 
{
	if (prefix.length() > length()) 
	    return false;
	return compare(0, prefix.length(), prefix) == 0;
}

bool EString::endsWith(const EString& suffix) const 
{
	if (suffix.length() > length()) 
	    return false;
	return compare(length() - suffix.length(), suffix.length(), suffix) == 0;
}

EString EString::toLower() const 
{
	EString result(*this);
	std::transform(result.begin(), result.end(), result.begin(),
 	    [](unsigned char c) { return std::tolower(c); });
	return result;
}

EString EString::toUpper() const 
{
	EString result(*this);
	std::transform(result.begin(), result.end(), result.begin(),
 	    [](unsigned char c) { return std::toupper(c); });
	return result;
}

EString EString::trimmed() const 
{
	auto front = std::find_if_not(begin(), end(), 
 	    [](int c) { return std::isspace(c); });
	auto back = std::find_if_not(rbegin(), rend(), 
	    [](int c) { return std::isspace(c); }).base();
	return (back <= front) ? EString() : EString(front, back);
}

EString EStringList::join(const EString& sep) const 
{
	if (empty()) 
	    return EString();

	EString result;
	auto it = begin();
	result = *it++;

	for (; it != end(); ++it) 
	{
		result += sep + *it;
	}

	return result;
}

bool EStringList::contains(const EString& str) const 
{
	return std::find(begin(), end(), str) != end();
}

EStringList& EStringList::removeDuplicates() 
{
	std::sort(begin(), end());
	erase(std::unique(begin(), end()), end());
	return *this;
}

EStringList& EStringList::sort() 
{
	std::sort(begin(), end());
	return *this;
}
